﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;
using System.IO;
using System.Net;
using System.Xml;
using System.Collections.Generic;
using System.Windows.Resources;
namespace FinosLibrary.Client {
    public partial class FinosLoader : UserControl {
        class ResFile {
            public static string baseURL;
            public string name;
            public string extra;
            public bool load;
            public string version;
            public bool needUpdate = false;
            public byte[] bytes;
            public string fullName { get { return name + '.' + extra; } }
            public string fullPath { get { return baseURL + '/' + fullName; } }
            public string zipPath { get { return baseURL + '/' + name + ".zip"; } }
        }
        private int _loadprogress;
        private int loadprogress {
            get { return _loadprogress; }
            set {
                _loadprogress = value;
                Storyboard1.Seek(new TimeSpan(2000 * value / totalNum));
                if (value == totalNum) {
                    Storyboard1.Stop();
                    Load(ResFiles.Where(resFile => resFile.load));
                    LoadCompleteEvent();
                }
            }
        }
        private int totalNum;
        private List<ResFile> ResFiles = new List<ResFile>();
        private List<ResFile> NeedReCreateFiles = new List<ResFile>();
        public delegate void LoadCompleteEventHandler();
        public event LoadCompleteEventHandler LoadCompleteEvent;
        public FinosLoader(LoadCompleteEventHandler complete) {
            // Required to initialize variables
            InitializeComponent();
            Storyboard1.Begin();
            LoadCompleteEvent += complete;
            WebClient wc = new WebClient();
            wc.OpenReadCompleted += new OpenReadCompletedEventHandler(List_OpenReadCompleted);
            wc.OpenReadAsync(new Uri("ResList.xml", UriKind.Relative));
        }
        private void List_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication()) {
                XmlReader xmlReader = XmlReader.Create(e.Result);
                Dictionary<string, string> oldFiles = new Dictionary<string, string>();
                if (store.FileExists("ResList.xml")) {
                    using (var stream = store.OpenFile("ResList.xml", FileMode.Open)) {
                        using (XmlReader oldReader = XmlReader.Create(stream)) {
                            oldReader.ReadToFollowing("List");
                            oldReader.MoveToAttribute("baseURL");
                            ResFile.baseURL = oldReader.Value;
                            while (oldReader.Read()) {
                                if (oldReader.AttributeCount < 1) continue;
                                string extraName = oldReader.LocalName;
                                oldReader.MoveToAttribute("n");
                                string fileName = oldReader.Value;
                                oldReader.MoveToAttribute("v");
                                string version = oldReader.Value;
                                oldFiles[fileName + '.' + extraName] = version;
                            }
                        }
                    }
                } else {
                    byte[] bytes = new byte[e.Result.Length];
                    e.Result.Seek(0, SeekOrigin.Begin);
                    e.Result.Read(bytes, 0, bytes.Length);
                    using(var stream = store.OpenFile("ResList.xml",FileMode.CreateNew)){
                        stream.Write(bytes, 0, bytes.Length);
                    }
                }
                xmlReader.ReadToFollowing("List");
                xmlReader.MoveToAttribute("baseURL");
                string _baseURL = xmlReader.Value;
                if (ResFile.baseURL != null && ResFile.baseURL != _baseURL) {
                    store.DeleteDirectory(ResFile.baseURL);
                }
                if (!store.DirectoryExists(_baseURL)) {
                    store.CreateDirectory(_baseURL);
                }
                ResFile.baseURL = _baseURL;
                while (xmlReader.Read()) {
                    if (xmlReader.AttributeCount < 1) continue;
                    string extraName = xmlReader.LocalName;
                    xmlReader.MoveToAttribute("n");
                    string fileName = xmlReader.Value;
                    xmlReader.MoveToAttribute("v");
                    string version = xmlReader.Value;
                    bool loadAtOnce = xmlReader.MoveToAttribute("l");
                    ResFile resFile = new ResFile { name = fileName, extra = extraName, load = loadAtOnce, version = version };
                    if (!oldFiles.ContainsKey(resFile.fullName) || oldFiles[resFile.fullName] != version) {
                        totalNum++;
                        resFile.needUpdate = true;
                    }
                    ResFiles.Add(resFile);
                }
                e.Result.Close();
            }
            if (totalNum == 0) {
                Storyboard1.Stop();
                Load(ResFiles.Where(resFile => resFile.load));
                LoadCompleteEvent();
            } else {
                UpdateFile(ResFiles.Where(resfile => resfile.needUpdate));
            }
        }

        private void UpdateFile(IEnumerable<ResFile> resFiles) {
            foreach (ResFile resFile in resFiles) {
                WebClient wc = new WebClient();
                wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
                wc.OpenReadAsync(new Uri(resFile.zipPath, UriKind.Relative), resFile);
            }
        }
        private void Load(IEnumerable<ResFile> resFiles) {
            foreach (ResFile resFile in resFiles) {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication()) {
                    using (var stream = store.OpenFile(resFile.fullPath, FileMode.Open)) {
                        new AssemblyPart().Load(stream);
                    }
                }
            }
        }

        void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) {
            ResFile resFile = e.UserState as ResFile;
            try {
                byte[] bytes;
                if (resFile.extra != "zip") {
                    StreamResourceInfo streamInfo = Application.GetResourceStream(new StreamResourceInfo(e.Result, "application/binary"), new Uri(resFile.fullName, UriKind.Relative));
                    bytes = new byte[streamInfo.Stream.Length];
                    streamInfo.Stream.Read(bytes, 0, bytes.Length);
                } else {
                    bytes = new byte[e.Result.Length];
                    e.Result.Read(bytes, 0, bytes.Length);
                }
                resFile.bytes = bytes;
                e.Result.Close();
                WriteFile(resFile);
                loadprogress++;
            } catch (IsolatedStorageException) {
                NeedReCreateFiles.Add(resFile);
                button.Visibility = Visibility.Visible;
                textBlock.Text = "存储空间不足！";
                buttonMotion.Begin();
            }
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication()) {
                if (store.IncreaseQuotaTo(store.Quota + NeedReCreateFiles.Sum(resfile => resfile.bytes.Length))) {
                    button.Visibility = Visibility.Collapsed;
                    textBlock.Text = "正在加载程序资源，请稍候...";
                    NeedReCreateFiles.ForEach(resFile => WriteFile(resFile));
                    loadprogress += NeedReCreateFiles.Count;
                    NeedReCreateFiles.Clear();
                }
            }
        }
        private void WriteFile(ResFile resFile) {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication()) {
                using (var stream = store.CreateFile(resFile.fullPath)) {
                    stream.Write(resFile.bytes, 0, resFile.bytes.Length);
                }
            }
        }
        public static Stream GetFile(string name) {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication()) {
                if (name.Contains('/')) {
                    StreamResourceInfo sri;
                    using(var stream = store.OpenFile(ResFile.baseURL + '/' + name.Split('/')[0]+".zip", FileMode.Open)){
                        sri = Application.GetResourceStream(new StreamResourceInfo(stream, "application/binary"), new Uri(name.Substring(name.IndexOf('/')+1), UriKind.Relative));
                    }
                    return sri.Stream;
                } else {
                    return store.OpenFile(ResFile.baseURL + '/' + name, FileMode.Open);
                }
            }
        }
    }
}